﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Ensynch.Configuration;
using Ensynch.DirectoryServices;
using NUnit.Framework;

namespace Ensynch.FIM
{
    [TestFixture]
    public class TestHarness
    {
        // Create a logger for use in this class.
        private static readonly log4net.ILog log =
            log4net.LogManager.GetLogger(
            System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private List<string> requestParams = new List<string>();

        public TestHarness()
        {
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""CreateRequestParameter""><Target>6ba9db42-6f11-42e8-9dd6-4a1b6f112eca</Target><Calculated>false</Calculated><PropertyName>DisplayName</PropertyName><Value xsi:type=""xsd:string"">asdf</Value><Operation>Create</Operation></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""CreateRequestParameter""><Target>6ba9db42-6f11-42e8-9dd6-4a1b6f112eca</Target><Calculated>false</Calculated><PropertyName>ObjectType</PropertyName><Value xsi:type=""xsd:string"">superTeamAssignment</Value><Operation>Create</Operation></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""CreateRequestParameter""><Target>6ba9db42-6f11-42e8-9dd6-4a1b6f112eca</Target><Calculated>false</Calculated><PropertyName>stEndDate</PropertyName><Value xsi:type=""xsd:dateTime"">2011-01-01T07:00:00</Value><Operation>Create</Operation></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""CreateRequestParameter""><Target>6ba9db42-6f11-42e8-9dd6-4a1b6f112eca</Target><Calculated>false</Calculated><PropertyName>stEffectiveDate</PropertyName><Value xsi:type=""xsd:dateTime"">2010-10-01T07:00:00</Value><Operation>Create</Operation></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""CreateRequestParameter""><Target>6ba9db42-6f11-42e8-9dd6-4a1b6f112eca</Target><Calculated>false</Calculated><PropertyName>fcAssigned</PropertyName><Value xsi:type=""q1:guid"">a4193e74-6ebf-4d22-90d6-e6159c363c04</Value><Operation>Create</Operation></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""UpdateRequestParameter""><Target>054e0d6c-3499-406a-bd2d-0d72e92519f8</Target><Calculated>false</Calculated><PropertyName>ecAssignedList</PropertyName><Value xsi:type=""q1:guid"">73dce7c6-6019-41b6-9973-6266dd75dd9c</Value><Operation>Create</Operation><Mode>Remove</Mode></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""UpdateRequestParameter""><Target>8f7e79c0-f4b3-4f6a-ac5d-91c9e51448a9</Target><Calculated>false</Calculated><PropertyName>ecAssignedList</PropertyName><Value xsi:type=""q1:guid"">ee805422-6039-4826-a388-cc1ce7eb1a7e</Value><Operation>Create</Operation><Mode>Remove</Mode></RequestParameter>");
            requestParams.Add(@"<RequestParameter xmlns:q1=""http://microsoft.com/wsdl/types/"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""UpdateRequestParameter""><Target>6db26562-e336-4aaf-b306-d9a187557feb</Target><Calculated>false</Calculated><PropertyName>ADmsExchHideFromAddressLists</PropertyName><Value xsi:type=""xsd:boolean"">true</Value><Operation>Create</Operation><Mode>Modify</Mode></RequestParameter>");
        }

        [SetUp]
        public void Init()
        {
            // Initialize class variables here.
            log4net.Config.XmlConfigurator.Configure(
                new System.IO.FileInfo(
                    System.Reflection.Assembly.GetExecutingAssembly().GetName().Name +
                    ".dll.config"));
        }

        private void logDefaultConfig()
        {
            var cfg = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None);
            log.Info("config file = " + cfg.FilePath);
            //StringBuilder buffer = new StringBuilder();
            //foreach (System.Configuration.ConfigurationSection section in cfg.Sections)
            //{
            //    buffer.AppendLine(section.SectionInformation.GetRawXml());
            //}
            //log.Info(buffer.ToString());
        }

        [Test]
        public void CanLogInfo()
        {
            log.Info("This is an info message.");
            logDefaultConfig();
        }

        [Test]
        public void CanLogWarning()
        {
            log.Warn("This is a warning message.");
        }

        [Test]
        public void CanLogError()
        {
            log.Error("This is an error message.");
        }

        [Test]
        public void TestQueryRequestParameters()
        {
            string xpath;
            List<object> queryResult;

            xpath = "//RequestParameter/Value";
            queryResult = FimTools.QueryRequestParameters(requestParams, xpath);
            Assert.True(queryResult.Contains("asdf"));
            Assert.True(queryResult.Contains(DateTime.Parse("2011-01-01T07:00:00")));
            Assert.True(queryResult.Contains(Boolean.Parse("true")));

            xpath = "//RequestParameter/Mode";
            queryResult = FimTools.QueryRequestParameters(requestParams, xpath);
            Assert.True(queryResult.Contains("Remove"));
            Assert.True(queryResult.Contains("Modify"));

            xpath = "//RequestParameter[(Mode='Remove')and(PropertyName='ecAssignedList')]/Value";
            queryResult = FimTools.QueryRequestParameters(requestParams, xpath);
            Assert.True(queryResult.Contains(new Guid("73dce7c6-6019-41b6-9973-6266dd75dd9c")));
            Assert.True(queryResult.Contains(new Guid("ee805422-6039-4826-a388-cc1ce7eb1a7e")));

        }

        [Test]
        public void TestExtractRequestParameterQuery()
        {
            string input = "Hello, World! [//RequestParameter[Mode='Remove']/Value] Hello, again! [//RequestParameter/Value] [//RequestParameter/Mode] Goodbye, World!";
            string query;
            FimTools.ExtractRequestParameterQuery(input, out query);
            Assert.AreEqual(query, "[//RequestParameter[Mode='Remove']/Value]");
        }

        [Test]
        public void TestResolveRequestParameterQueries()
        {
            string input = "Hello, World! [//RequestParameter[Mode='Remove']/Value] Hello, again! [//RequestParameter/Value] [//RequestParameter/Mode] Goodbye, World!";
            FimTools.ResolveRequestParameterQueries(input, ref requestParams);
        }

        [Test]
        public void TestExtractAllXPathGrammar()
        {
            string input =
@"Hello, World! [//RequestParameter[Mode='Remove']/Value] 
Hello, again! [//RequestParameter/Value][//RequestParameter/Mode] 
[//RequestParameter[(Mode='Remove') and (PropertyName='ecAssignedList')]/Value]
Goodbye, World!";
            List<string> grammar = new List<string>();
            FimTools.ExtractAllXPathGrammar(input, ref grammar);
            Assert.True(grammar.Contains("[//RequestParameter[(Mode='Remove') and (PropertyName='ecAssignedList')]/Value]"));

            grammar = new List<string>();
            input =
@"[//RequestParameter[(Mode='Remove') and (PropertyName='ecAssignedList')]/Value]";
            FimTools.ExtractAllXPathGrammar(input, ref grammar);
            Assert.True(grammar.Contains("[//RequestParameter[(Mode='Remove') and (PropertyName='ecAssignedList')]/Value]"));

            grammar = new List<string>();
            input =
@"[//RequestParameter[(Mode='Remove') and (PropertyName='ecAssignedList')]";
            Assert.False(FimTools.ExtractAllXPathGrammar(input, ref grammar));
        }

        public void CanParseSql()
        {
            string[] tests = new string[]
            {
                " /object[attr = 'something'] "
                ," Select top 10 blah, foo,bar from /xyz where abc order by 123"
                ,"select * from /blah"
                ,"Select foo,bar from /blah order by abc"
                ,"Select foo,bar from /blah where 1 + 1 = 2"
                ,"Select foo,bar from /blah where 1 + 1 = 2 order by abc"
                ,"select foo, bar from /blah[blah] where a2 + b2 = c@ + 4ab cos(g) order by abc desc"
                ,@"Select  foo,bar    ,blah  From /object[attr = 'something']  Where this = that  Order by whatever desc"
,@"Select TOP 10
    foo,bar
    ,blah
From /object[attr = 'something']
Where this = that
Order by whatever desc
"
            };
            foreach (string test in tests)
            {
                FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            }
        }

        [Test]
        public void CanParseQuery1()
        {
            string test = " /object[attr = 'something'] ";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(test.Trim(), fimQuery.XPath);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery2()
        {
            string test = " Select top 10 blah, foo,bar from /xyz where 1 + 1 = 2 order by 123";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(10, fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.Contains("blah", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/xyz");
            Assert.AreEqual(fimQuery.WhereConditions, "1 + 1 = 2");
            Assert.Contains(new KeyValuePair<string, bool>("123", true), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery3()
        {
            string test = "select * from /blah";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.IsNull(fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/blah");

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery3a()
        {
            string test = "select * from /blah";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.IsNull(fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/blah");

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery4()
        {
            string test = "Select foo,bar from /blah order by abc";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.IsNull(fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/blah");
            Assert.IsNull(fimQuery.WhereConditions);
            Assert.Contains(new KeyValuePair<string, bool>("abc", true), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery5()
        {
            string test = "Select foo,bar from /blah where 1 + 1 = 2";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.IsNull(fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/blah");
            Assert.AreEqual(fimQuery.WhereConditions, "1 + 1 = 2");
            Assert.IsNull(fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery6()
        {
            string test = "select foo, bar from /blah[blah] where a2 + b2 = c@ + 4ab cos(g) order by abc desc";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.IsNull(fimQuery.MaxRows);
            Assert.AreEqual(fimQuery.XPath, "/blah[blah]");
            Assert.AreEqual(fimQuery.WhereConditions, "a2 + b2 = c@ + 4ab cos(g)");
            Assert.Contains(new KeyValuePair<string, bool>("abc", false), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery7()
        {
            string test = "Select  TOP 10  foo,bar    ,blah  From /object[attr = 'something']  Where this = that  Order by whatever desc";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(10, fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.Contains("blah", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/object[attr = 'something']");
            Assert.AreEqual(fimQuery.WhereConditions, "this = that");
            Assert.Contains(new KeyValuePair<string, bool>("whatever", false), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery8()
        {
            string test =
@"Select  TOP 10  
    foo,bar    
    ,blah  
From /object[attr = 'something']  
Where this = that  
Order by whatever desc
";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(10, fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.Contains("blah", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/object[attr = 'something']");
            Assert.AreEqual(fimQuery.WhereConditions, "this = that");
            Assert.Contains(new KeyValuePair<string, bool>("whatever", false), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQuery9()
        {
            string test =
@"Select  TOP 10  
    foo,bar    
    ,blah,
    column1,
    column2a as name,
    column2b as [name],
     [column3],
     [column4] as name,
     [column5] as [name]
,    column6
,    column7 as name
,     [column8]
,     [column9] as name
,     [column10] as [name]
, [as] as [Joe's place, inc.]
, [From] as [From]

From /object[attr = 'something']  
Where this = that  
Order by whatever desc
";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(10, fimQuery.MaxRows);
            Assert.Contains("foo", fimQuery.EnumerateAttributes);
            Assert.Contains("bar", fimQuery.EnumerateAttributes);
            Assert.Contains("blah", fimQuery.EnumerateAttributes);
            Assert.AreEqual(fimQuery.XPath, "/object[attr = 'something']");
            Assert.AreEqual(fimQuery.WhereConditions, "this = that");
            Assert.Contains(new KeyValuePair<string, bool>("whatever", false), fimQuery.SortAttributes);

            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQueryA()
        {
            string test =
@"SELECT TOP 100
	DisplayName
	,CreatedTime
	,Creator
	,Operation
	,Target
	,TargetObjectType
	,RequestParameter
	,RequestStatus
	,RequestStatusDetail

FROM /Request[(RequestStatus = 'Denied') or (RequestStatus = 'Failed') or (RequestStatus = 'PostProcessingError')]

ORDER BY CreatedTime DESC
";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(100, fimQuery.MaxRows);
            Assert.AreEqual(9, fimQuery.EnumerateAttributes.Length);
            Assert.AreEqual(fimQuery.XPath, "/Request[(RequestStatus = 'Denied') or (RequestStatus = 'Failed') or (RequestStatus = 'PostProcessingError')]");
            Assert.Contains(new KeyValuePair<string, bool>("CreatedTime", false), fimQuery.SortAttributes);
            log.Debug(fimQuery.ToString());
        }

        [Test]
        public void CanParseQueryB()
        {
            string test =
@"Select
    DataType Data Type
	,DeletedTime
	,Description AS [Description]
	,DetectedRulesList AS [Detected Rules List]
	,DisplayName AS [Display Name]
	,ExpectedRulesList AS [Expected Rules List]
	,ExpirationTime AS [Expiration Time]
	,IntegerMaximum AS [Integer Maximum]
	,IntegerMinimum AS [Integer Minimum]
	,Locale AS [Locale]
	,Localizable AS [Localizable]
	,Multivalued AS [Multivalued]
	,MVObjectID AS [MV Resource ID]
	,Name AS [Name]
	,ObjectID AS [Resource ID]
	,ResourceTime AS [Resource Time]
	,ObjectType AS [Resource Type]
	,StringRegex AS [String Regular Expression]
	,UsageKeyword AS [Usage Keyword]
	,Creator AS [Joe's Place, Inc.]
From /BindingDescription[BoundObjectType = /ObjectTypeDescription[Name = 'Person']]/BoundAttributeType";
            FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            Assert.AreEqual(20, fimQuery.EnumerateAttributes.Length);
            log.Debug(fimQuery.ToString());
        }

//        [Test]
//        [ExpectedException(typeof(System.Data.SyntaxErrorException))]
//        public void QueryShouldFail1()
//        {
//            try
//            {
//                string test =
//@"Select  TOP 10  
//    foo,bar    
//    ,blah  
//From Person  
//Where this = that  
//Order by whatever desc
//";
//                FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
//            }
//            catch (Exception exc)
//            {
//                log.Error("[QueryShouldFail]", exc);
//                throw exc;
//            }
//        }

        [Test]
        [ExpectedException(typeof(System.Data.SyntaxErrorException))]
        public void QueryShouldFail2()
        {
            try
            {
                string test =
@"Select  TOP 10  
    foo,bar    
    ,blah  
From /Person  
Where this = that  
Order by whatever, whenever desc
";
                FimQuery fimQuery = new FimQuery(test, FimTools.DefaultInstance);
            }
            catch (Exception exc)
            {
                log.Error("[QueryShouldFail]", exc);
                throw exc;
            }
        }

    }
}
